package mlogger

import (
	"fmt"
	"gitee.com/dennis-mxx/mxx-core/mexception"
	"gitee.com/dennis-mxx/mxx-core/msystem"
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"io"
	"os"
	"runtime/debug"
	"strings"
	"time"
)

var Logger *zap.SugaredLogger

const (
	outputDir = "./logs/"
	infoPath  = "info.log"
	debugPath = "debug.log"
	errPath   = "error.log"
)

func InitZap() {
	var level zapcore.Level
	switch msystem.Log.Level {
	case "debug":
		level = zapcore.DebugLevel
		break
	case "error":
		level = zapcore.ErrorLevel
		break
	case "warn":
		level = zapcore.WarnLevel
		break
	default:
		level = zapcore.InfoLevel
		break
	}

	// 设置一些基本日志格式 具体含义还比较好理解，直接看zap源码也不难懂
	encoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
		MessageKey:  "msg",
		LevelKey:    "level",
		TimeKey:     "time",
		CallerKey:   "file",
		NameKey:     "logger",
		LineEnding:  zapcore.DefaultLineEnding,
		EncodeLevel: zapcore.CapitalLevelEncoder,
		//EncodeLevel:    zapcore.CapitalLevelEncoder,
		EncodeCaller: zapcore.ShortCallerEncoder,
		EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
			enc.AppendString(t.Format("2006-01-02 15:04:05"))
		},
		//EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) {
			enc.AppendInt64(int64(d) / 1000000)
		},
	})

	// 实现两个判断日志等级的interface
	logLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= level
	})

	infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.InfoLevel
	})

	errorLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl == zapcore.ErrorLevel
	})
	debugLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl == zapcore.DebugLevel
	})

	// 获取 info、warn日志文件的io.Writer 抽象 getWriter() 在下方实现

	infoHook := getWriter(infoPath)
	debugHook := getWriter(debugPath)
	errorHook := getWriter(errPath)

	// 最后创建具体的Logger
	core := zapcore.NewTee(
		zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), logLevel),
		zapcore.NewCore(encoder, zapcore.AddSync(infoHook), infoLevel),
		zapcore.NewCore(encoder, zapcore.AddSync(debugHook), debugLevel),
		zapcore.NewCore(encoder, zapcore.AddSync(errorHook), errorLevel),
	)

	// 需要传入 zap.AddCaller() 才会显示打日志点的文件名和行数, 有点小坑
	log := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))
	Logger = log.Sugar()
	defer log.Sync()
}

func getWriter(filename string) io.Writer {
	hook := zapcore.AddSync(&lumberjack.Logger{
		Filename:   outputDir + filename, //日志文件存放目录
		MaxSize:    15,                   //文件大小限制,单位MB
		MaxBackups: 5,                    //最大保留日志文件数量
		MaxAge:     15,                   //日志文件保留天数
		Compress:   true,                 //是否压缩处理
	})

	return hook
}

func FormatException() {
	defer func() {
		if error := recover(); error != nil {
			Logger.Error(error)
		}
	}()
	if error := recover(); error != nil {
		FormatError(error)
	}

}
func FormatError(error interface{}) {
	if error == nil {

	}
	stack := debug.Stack()
	var newStack []byte

	for newStackIndex, count, index := 0, 0, 0; index <= len(stack)/2; index++ {
		current := stack[index]
		if count <= 6 && current == 10 {
			count++
			continue
		}
		if count > 6 {
			if newStack == nil {
				newStack = make([]byte, len(stack)/2-index+1)
			}
			newStack[newStackIndex] = current
			newStackIndex++

			if current == 10 {
				newStack[newStackIndex] = 9
				newStackIndex++
				index++
			}
		}
	}

	message := strings.Trim(string(newStack), " ")
	var pr string
	if mexception.IsMException(error) {
		pr = fmt.Sprintf("\n\t%s\n\t%s0x1B", error, message)
	} else {
		pr = fmt.Sprintf("\n\tExceltion:\t【%s】\n\t%s0x1B", error, message)
	}
	Logger.Error(pr)

}
